package com.runvp.service.biz.handler;

import com.alibaba.fastjson.JSONObject;
import com.runvp.common.config.WechatConfigure;
import com.runvp.common.constant.ConstantRedisKey;
import com.runvp.common.core.redis.RedisCache;
import com.runvp.common.enums.BillChangeType;
import com.runvp.common.enums.PayChannel;
import com.runvp.common.enums.TranType;
import com.runvp.common.enums.YesOrNo;
import com.runvp.common.exception.base.BaseException;
import com.runvp.common.pay.MainPay;
import com.runvp.common.pay.OrderCreateModel;
import com.runvp.common.pay.wechat.NativeClient;
import com.runvp.common.util.MoneyUtils;
import com.runvp.common.util.OrderNoUtils;
import com.runvp.common.util.auth.SecurityUtils;
import com.runvp.common.utils.DateUtils;
import com.runvp.common.utils.Threads;
import com.runvp.common.utils.http.HutoolHttpClient;
import com.runvp.common.utils.validtion.CheckException;
import com.runvp.service.biz.domain.BizCustomerRecharge;
import com.runvp.service.biz.domain.BizCustomerTranLog;
import com.runvp.service.biz.service.IBizCustomerRechargeService;
import com.runvp.service.biz.service.IBizCustomerWalletService;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class WechatPayHandler {

    @Autowired
    private RedisCache redisUtil;
    @Autowired
    private IBizCustomerWalletService bizCustomerWalletService;
    @Autowired
    private IBizCustomerRechargeService bizCustomerRechargeService;

    /**
     * 新增用户充值
     *
     * @param amount 用户充值
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String,Object> recharge(BigDecimal amount){
        BizCustomerRecharge recharge = new BizCustomerRecharge();
        recharge.setAppId(WechatConfigure.getApp_id());
        recharge.setGoodsTitle("用户充值");
        recharge.setGoodsDesc("用户充值");
        recharge.setCustomerId(SecurityUtils.getUserId());
        recharge.setOpenId(SecurityUtils.getUserInfo().getOpenId());
        recharge.setCustomerPhone(SecurityUtils.getUserInfo().getPhone());
        recharge.setOrderNo(OrderNoUtils.getOrderNo());
        recharge.setPayAmt(amount);
        recharge.setCreateBy(SecurityUtils.getStrUserId());
        bizCustomerRechargeService.insertBizCustomerRecharge(recharge);

        //构建支付信息，如果失败则下单动作结束
        Map<String,Object> map = new HashMap<>();
        map.put("status","success");
        OrderCreateModel model = new OrderCreateModel();
        model.setOpenId(recharge.getOpenId());
        model.setOrderNo(recharge.getOrderNo());
        model.setPayAmt(String.valueOf(recharge.getPayAmt()));
        model.setGoodsTitle(recharge.getGoodsTitle());
        model.setGoodsDesc(recharge.getGoodsDesc());
        JSONObject json = MainPay.merchantPay(model);
        if(CollectionUtils.isEmpty(json)){
            throw new BaseException("订单创建失败,请稍后重试!");
        }
        setPayStatus(model.getOrderNo(),json.getString("queryUrl"));
        json.put("orderNo",model.getOrderNo());
        map.putAll(json);
        return map;
    }

    /**
     * 查询充值状态
     * @param orderNo
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> rechargeStatus(String orderNo){
        Map<String, Object> resMap = new HashMap<>();
        //支付状态
        JSONObject payStatus = getPayStatus(orderNo);
        //pending :交易处理中,succeeded :交易成功,failed :交易失败
        String status = payStatus.getString("status");
        if(!"succeeded".equals(status)){
            resMap.put("code","500");
            resMap.put("message","支付状态【"+status+"】");
            if("failed".equals(status)){
                resMap.put("message",payStatus.getString("error_msg"));
                BizCustomerRecharge recharge = bizCustomerRechargeService.selectBizCustomerRechargeByOrderNo(orderNo);
                CheckException.emptyThrowException(recharge,"充值失败:未找到【"+orderNo+"】的充值记录");
            }
            return resMap;
        }
        //处理支付成功逻辑
        BizCustomerRecharge recharge = bizCustomerRechargeService.selectBizCustomerRechargeByOrderNo(orderNo);
        CheckException.emptyThrowException(recharge,"充值失败:未找到【"+orderNo+"】的充值记录");
        //状态不为空，则认为结束
        if(!"".equals(recharge.getStatus())){
            throw new BaseException("该记录已完成,不可操作");
        }
        BizCustomerTranLog bizCustomerTranLog = new BizCustomerTranLog();
        bizCustomerTranLog.setCustomerId(SecurityUtils.getUserId());
        bizCustomerTranLog.setTranFee(recharge.getPayAmt());
        bizCustomerTranLog.setPayFee(recharge.getPayAmt());
        bizCustomerTranLog.setTranNo(recharge.getOrderNo());
        bizCustomerTranLog.setTranType(TranType.RECHARGE.getCode());
        bizCustomerTranLog.setBillChangeType(BillChangeType.IN.getCode());
        bizCustomerTranLog.setPayChannel(PayChannel.PAY_WECHAT.getCode());
        bizCustomerTranLog.setCreateBy(SecurityUtils.getStrUserId());
        bizCustomerTranLog.setRemark(TranType.RECHARGE.getDesc());
        bizCustomerWalletService.rechargeSave("用户充值:",bizCustomerTranLog);
        recharge.setStatus(YesOrNo.YES.getCode());
        recharge.setResData(JSONObject.toJSONString(payStatus));
        recharge.setRemark("充值成功,时间:"+ DateUtils.getTime());
        recharge.setUpdateBy(SecurityUtils.getStrUserId());
        bizCustomerRechargeService.updateBizCustomerRecharge(recharge);
        //构建返回信息给支付平台
        resMap.put("code", "200");
        resMap.put("message", "订单完成");
        return resMap;
    }
    /**
     * 检测支付状态
     * @param orderNo
     * @param queryUrl
     */
    public void setPayStatus(String orderNo,String queryUrl){
        redisUtil.setCacheObjectSeconds(ConstantRedisKey.ORDER_PAY_STATUS+orderNo,queryUrl,86400);
    }

    /**
     * 获取支付状态 -三方支付
     * @param orderNo
     * @return json
     */
    public JSONObject getPayStatus(String orderNo){
        String queryUrl;
        String status;
        JSONObject payStatus;
        for (int i = 0; i < 6; i++) {
            queryUrl = redisUtil.getCacheObject(ConstantRedisKey.ORDER_PAY_STATUS+orderNo);
            if(StringUtils.isBlank(queryUrl)){
                continue;
            }
            payStatus = JSONObject.parseObject(HutoolHttpClient.get(queryUrl));
            log.info("getPayStatus三方支付结果查询第{}次:{}",(i+1),payStatus);
            //pending :交易处理中,succeeded :交易成功,failed :交易失败
            status = payStatus.getString("status");
            if(!"pending".equals(status)){
                return payStatus;
            }
            Threads.sleep(500);
        }
        payStatus = new JSONObject();
        payStatus.put("status","failed");
        payStatus.put("error_msg","未查到支付信息");
        return payStatus;
    }

    /**
     * 获取支付状态 -微信原生支付
     * @param orderNo
     * @return json
     */
    public JSONObject getWechatPayStatus(String orderNo){
        NativeClient client = new NativeClient();
        JSONObject payStatus;
        for (int i = 0; i < 6; i++) {
            Transaction transaction = client.queryOrderByOutOrderNo(orderNo);
            if(null == transaction){
                continue;
            }
            if(Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())){
                payStatus = new JSONObject();
                payStatus.put("status","succeeded");
                payStatus.put("pay_amt", MoneyUtils.pointTOBigDecimal(transaction.getAmount().getTotal()));
                return payStatus;
            }
            log.info("getWechatPayStatus微信原生支付结果查询第{}次:{}",(i+1),JSONObject.toJSONString(transaction));
            Threads.sleep(500);
        }
        payStatus = new JSONObject();
        payStatus.put("status","failed");
        payStatus.put("error_msg","未查到支付信息");
        return payStatus;
    }
}
